import { Geometry } from './geometry/geometry'

/**
 * @ClassName map
 * @Description 地图对象
 * @Author xuyizhuo
 * @Date 2024/1/20 14:21
 */
export class Map {
    // Canavas画布
    _canvas: HTMLCanvasElement
    // Canvas上下文
    _ctx: CanvasRenderingContext2D
    // 地图中的图像对象
    _geometries: Geometry[] = []

    // 画布的移动状态
    _drag = {
        flag: false,
        start: {
            x: 0,
            y: 0,
        },
        end: {
            x: 0,
            y: 0,
        },
    }

    constructor(id: string) {
        // 获取到画布、上下文
        this._canvas = document.getElementById(id) as HTMLCanvasElement
        this._ctx = this._canvas.getContext('2d')

        // bind() 方法创建一个新函数，当调用该新函数时，它会调用原始函数并将其 this 关键字设置为给定的值
        this._onDoubleClick = this._onDoubleClick.bind(this)
        this._onMouseDown = this._onMouseDown.bind(this)
        this._onMouseMove = this._onMouseMove.bind(this)
        this._onMouseUp = this._onMouseUp.bind(this)
        this._onWheel = this._onWheel.bind(this)

        // 绑定地图(canvas画布)的操作事件
        this._canvas.addEventListener('dblclick', this._onDoubleClick) // 鼠标双击
        this._canvas.addEventListener('mousedown', this._onMouseDown) // 鼠标按下
        this._canvas.addEventListener('mousemove', this._onMouseMove) // 鼠标移动
        this._canvas.addEventListener('mouseup', this._onMouseUp) // 鼠标弹起
        this._canvas.addEventListener('wheel', this._onWheel) // 鼠标滚轮缩放
    }

    // 添加图形
    addGeometry(geometry: Geometry) {
        this._geometries.push(geometry)
        geometry.draw(this._ctx)
    }

    // 重绘：地图缩放、移动时
    redraw() {
        // 清空画布
        this._ctx.save()
        this._ctx.setTransform(1, 0, 0, 1, 0, 0)
        this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height)
        this._ctx.restore()
        // 重新绘制图形
        for (let i = 0; i < this._geometries.length; i++) {
            this._geometries[i].draw(this._ctx)
        }
    }

    // 清空绘制
    clear() {
        this._ctx.setTransform(1, 0, 0, 1, 0, 0)
        this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height)
    }

    // 鼠标双击：缩放
    _onDoubleClick() {
        this._ctx.scale(2, 2)
        this.redraw()
    }

    // 鼠标按下
    _onMouseDown(event: MouseEvent) {
        this._drag.flag = true
        this._drag.start.x = event.x
        this._drag.start.y = event.y
    }

    // 鼠标移动
    _onMouseMove() {
    }

    // 鼠标弹起
    _onMouseUp(event: MouseEvent) {
        if (this._drag.flag) {
            this._drag.flag = false
            this._drag.end.x = event.x
            this._drag.end.y = event.y
            const matrix = this._ctx.getTransform()
            this._ctx.translate((this._drag.end.x - this._drag.start.x) / matrix.a, (this._drag.end.y - this._drag.start.y) / matrix.d)
            this.redraw()
        }
        this._drag.flag = false
    }

    private _wheelTimer: any

    // 鼠标滚轮缩放
    _onWheel(event: WheelEvent) {
        event.preventDefault()
        event.stopPropagation()
        // 防止抖动
        this._wheelTimer && clearTimeout(this._wheelTimer)
        this._wheelTimer = setTimeout(() => {
            let scale = 1
            const sensitivity = 100 // 鼠标滚轮的敏感度
            const delta = (event as any).wheelDelta / sensitivity
            // 判断滚轮是放大还是缩小
            if (delta > 0) {
                // 放大
                scale *= delta * 2
            } else {
                // 缩小
                scale /= delta * -2
            }

            // 交互表现为：鼠标当前位置，屏幕坐标不变，进行缩放。即：x2 = x1，y2=y1，变换前坐标（x1, y1），变换后坐标（x2, y2）
            // 其它设定：变换前矩阵(a1,0,0,d1,e1,f1)   变换矩阵(a,0,0,d,e,f)  变换后矩阵(a2,0,0,d2,e2,f2)
            // scale已通过滚轮变化，换算得到，且a=d=scale，求e和f
            // 1.将原屏幕坐标 x1 转成 地理坐标 x0 = (x1 - e1) / a1
            // 2.地理坐标x0 转成 现屏幕坐标x2  a2 * x0 + e2 = x2，e2 = x2 - a2 * x0，代入1式 e2 = x2 - a2 * (x1 - e1) / a1
            // 3.已知scale = a2 / a1 故 e2 = x2 - scale * (x1 - e1)
            // 4.另矩阵变换 a1 * e + e1 = e2
            // 5.联立3和4 求得 e = (x2 - scale * (x1 - e1) - e1) / a1

            const matrix = this._ctx.getTransform()
            const a1 = matrix.a, e1 = matrix.e, x1 = event.offsetX, x2 = x1 // 放大到中心点 x2 = this._canvas.width / 2
            const e = (x2 - scale * (x1 - e1) - e1) / a1
            const d1 = matrix.d, f1 = matrix.f, y1 = event.offsetY, y2 = y1 // 放大到中心点 y2 = this._canvas.height / 2
            const f = (y2 - scale * (y1 - f1) - f1) / d1
            this._ctx.transform(scale, 0, 0, scale, e, f)

            this.redraw()
        }, 50)
    }

    // 销毁
    destroy() {
        // 移除画布事件
        this._canvas.removeEventListener('dblclick', this._onDoubleClick) // 鼠标双击
        this._canvas.removeEventListener('mousedown', this._onMouseDown) // 鼠标按下
        this._canvas.removeEventListener('mousemove', this._onMouseMove) // 鼠标移动
        this._canvas.removeEventListener('mouseup', this._onMouseUp) // 鼠标弹起
        this._canvas.removeEventListener('wheel', this._onWheel) // 鼠标滚轮缩放
    }
}
